/*
 * BaseStation.java
 *
 * By Gary Wong, 2005.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of Version 2 of the GNU General Public 
 * License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

package sxe.sensors;

import java.io.IOException;
import java.net.UnknownHostException;
import java.net.Socket;
import java.util.Hashtable;

public class BaseStation {
    protected class Reading {
	public long time;
	public int value;
    }

    protected Socket socket;
    protected java.io.InputStream inputStream;
    protected Hashtable<Integer,Reading> readings;

    private class ListenerThread extends Thread {
	public void run() {
	    for(;;) {
		try {
		    int i, length, read;

		    if( ( length = inputStream.read() & 0xFF ) < 0 )
			throw new IOException( "Connection closed" );

		    byte[] data = new byte[ length ];

		    for( i = 0; i < length; ) {
			if( ( read = inputStream.read( data, i, 
						       length - i ) ) < 0 )
			    throw new IOException( "Connection closed" );

			i += read;
		    }

		    /*
		     * Packet format:
		     *
		     * 0-1: destination (0x007E - UART)
		     * 2: Active Message type (0x11 - Surge)
		     * 3: group ID (0x5E)
		     * 4: length
		     * 5-6: source address (0x00 - root)
		     * 7-8: origin address
		     * 9-10: sequence number
		     * 11: hop count
		     * 12: Surge type (0x00 - sensor reading)
		     * 13-14: sensor value
		     * 15-16: parent address
		     */
		    if( length < 17 ||
			data[ 0 ] != 0x7E ||
			data[ 1 ] != 0x00 ||
			data[ 2 ] != 0x11 ||
			data[ 3 ] != 0x5E ||
			data[ 4 ] < 12 ||
			data[ 5 ] != 0x00 ||
			data[ 6 ] != 0x00 ||
			data[ 12 ] != 0x00 )
			/* not a valid sensor reading -- ignore */
			continue;

		    int sensor = data[ 7 ] & 0xFF | ( data[ 8 ] & 0xFF ) << 8;
		    int value = data[ 13 ] & 0xFF | ( data[ 14 ] & 0xFF ) << 8;

		    Reading reading = new Reading();
		    reading.time = System.nanoTime();
		    reading.value = value;

		    synchronized( this ) {
			readings.put( sensor, reading );
		    }
		} catch( Exception ex ) {
		    System.exit( 1 );
		}
	    }
	}
    }
    
    public BaseStation( String host, int port ) throws UnknownHostException,
	IOException {
	java.io.OutputStream outputStream;

	socket = new Socket( host, port );
	inputStream = socket.getInputStream();
	outputStream = socket.getOutputStream();
	
	/* send the server our protocol version */
	outputStream.write( 'T' );
	outputStream.write( ' ' );

	/* get the server's protocol version */
	if( inputStream.read() != 'T' ||
	    inputStream.read() != ' ' )
	    throw new IOException( "Incompatible server version" );

	readings = new Hashtable<Integer,Reading>();

	ListenerThread listener = new ListenerThread();
	
	listener.setDaemon( true );
	listener.start();
    }

    /* Returns the age (in nanoseconds) of the value from the specified
       sensor, or -1 if no value is available. */
    public synchronized long getAge( int sensor ) {
	Reading reading = readings.get( sensor );

	if( reading == null )
	    return -1;
	else
	    return System.nanoTime() - reading.value;
    }

    public synchronized int getValue( int sensor ) {
	Reading reading = readings.get( sensor );

	if( reading == null )
	    return -1;
	else
	    return reading.value;
    }
}
